﻿//#define BORDER_DEBUG

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using MineFlagger;
using System.Collections;

namespace MineFlagger
{
    public partial class GameBoardView : Control, IViewer, INotifyPropertyChanged {
    

        
        GameBoard gameBoard;

        int fieldHeight;
        int fieldWidth;
        int borderThickness;
        int borderWidth;
        int borderHeight;


        public GameBoardView()
        {
            InitializeComponent();

            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);

            setupStaticDrawingResources();

        }


        
        public GameBoard GameBoard
        {
            get { return gameBoard; }
            set
            {
                if (gameBoard == value)
                    return;

                gameBoard = value;

                gameBoard.PropertyChanged += new PropertyChangedEventHandler(gameBoard_PropertyChanged);
                gameBoard.InvalidateDirtyCells += new EventHandler(gameBoard_InvalidateDirtyCells);

                validateDimensions();
            }
        }

        void gameBoard_InvalidateDirtyCells(object sender, EventArgs e)
        {
            InvalidateDirtyCells();
        }

        void gameBoard_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "PlayState")
            {
                if (gameBoard.PlayState == PlayState.New)
                    validateDimensions();
            }
        }





        void validateDimensions()
        {

            if (gameBoard == null || fieldHeight == gameBoard.Height && fieldWidth == gameBoard.Width)
                return;

            fieldHeight = gameBoard.Height;
            fieldWidth = gameBoard.Width;
            
            borderThickness = 3;
            borderWidth = (fieldWidth * width) + borderThickness * 2 + 1;
            borderHeight = (fieldHeight * width) + borderThickness * 2 + 1;


            this.Height = borderHeight;
            this.Width = borderWidth;

            drawGameGridBMP();

            Invalidate();
        }

        void setupStaticDrawingResources()
        {
            createStringBrushes();

            drawHiddenCellBMP();
            drawNumberCellBMP();
            drawFlagBMP();
            drawFlag2BMP();
            drawMineBMP();
        }






        public event EventHandler UserChangedBoard;
        protected void OnUserChangedBoard()
        {
            if (UserChangedBoard != null)
                UserChangedBoard(this, new EventArgs());
        }












        #region GDI+ Rendering

        // Application drawing vars
        int xOff = 3; // hack: todo: rework to use single class-based offsets and boarders
        int yOff = 3;
        int width = 16;

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            if (gameBoard == null)
                return;

            RenderGraphicsGDI(null, e);
        }




        Bitmap gameGridBMP;


        void drawGameGridBMP()
        {


            // border
            int borderThickness = 3;
            int left = 0;
            int top = 0;
            int borderWidth = (fieldWidth * width) + borderThickness * 2;
            int borderHeight = (fieldHeight * width) + borderThickness * 2;
            int right = left + borderWidth;
            int bottom = top + borderHeight;



            // create bitmap
            if (gameGridBMP != null) gameGridBMP.Dispose();
            gameGridBMP = new Bitmap(borderWidth + 1, borderHeight + 1);
            Graphics g = Graphics.FromImage(gameGridBMP);


            {  // Grid

                int gridLeft = left + borderThickness;
                int gridTop = top + borderThickness;
                int gridRight = right - borderThickness;
                int gridBottom = bottom - borderThickness;

                Pen gridPen = new Pen(Color.DarkGray, 1.0f);

                // vertical lines
                for (int xCell = 0; xCell <= fieldWidth; xCell++)
                {
                    int x = gridLeft + (xCell * width);
                    g.DrawLine(gridPen, x, gridTop, x, gridBottom);
                }

                // horizontal lines
                for (int yCell = 0; yCell <= fieldWidth; yCell++)
                {
                    int y = gridTop + (yCell * width);
                    g.DrawLine(gridPen, gridLeft, y, gridRight, y);
                }

                gridPen.Dispose();
            }

            { // border

                Brush backBrush, borderBrushUpper, borderBrushLower;

#if !BORDER_DEBUG
                borderBrushUpper = Brushes.DarkGray; 
                borderBrushLower = Brushes.White; 
                backBrush = new SolidBrush(BackColor);
#else
                borderBrushUpper = Brushes.Purple;
                borderBrushLower = Brushes.Green; 
                backBrush = Brushes.Red;
#endif

                // gray top and left border
                g.FillRectangle(borderBrushUpper, left, top, borderWidth, borderThickness); // TOP
                g.FillRectangle(borderBrushUpper, left, top, borderThickness, borderHeight); // LEFT

                // white bottom and right 
                g.FillRectangle(borderBrushLower, left + 1, bottom - borderThickness + 1, borderWidth, borderThickness); // BOTTOM
                g.FillRectangle(borderBrushLower, right - borderThickness + 1, top + 1, borderThickness, borderHeight - 2); // RIGHT


                // upper right corner touch up
                g.FillRectangle(backBrush, right - borderThickness + 2, top + 1, 1, 1); // diag pix
                g.FillRectangle(backBrush, right - borderThickness + 1, top + 2, 1, 1); // diag pix
                g.FillRectangle(borderBrushUpper, right - borderThickness + 1, top + 1, 1, 1); // fix overlap of left border on top

                // lower left corner touch up
                g.FillRectangle(backBrush, left + 2, bottom - borderThickness + 1, 1, 1); // diag pix
                g.FillRectangle(backBrush, left + 1, bottom - borderThickness + 2, 1, 1); // diag pix
                g.FillRectangle(borderBrushUpper, left + 1, bottom - borderThickness + 1, 1, 1); // fix overlap of bottom border on left


#if !BORDER_DEBUG
                backBrush.Dispose();
#endif
            }

            g.Dispose();
        }




        public void RenderGraphicsGDI(object sender, System.Windows.Forms.PaintEventArgs e)
        {


            if (gameBoard.PlayState == PlayState.Lost)
            {
                int xCur = gameBoard.CellThatExploded.X;
                int yCur = gameBoard.CellThatExploded.Y;
                e.Graphics.FillRectangle(Brushes.Red, (xCur * width) + xOff, (yCur * width) + yOff, width, width);
            }


            /* draw game grid */
            e.Graphics.DrawImage(gameGridBMP, xOff - 3, yOff - 3); 



            /* draw cells that fall within invalid region */

            int xStart, yStart, xEnd, yEnd;

            int normLeft, normTop;
            int cellsDrawn = 0;

            if (e.ClipRectangle.Width % width == 0 && (e.ClipRectangle.X - xOff) % width == 0 &&
                e.ClipRectangle.Height % width == 0 && (e.ClipRectangle.Y - yOff) % width == 0)
            {
                // ClipRectangle lines up perfectly on cell boundries (usal case). Calculate
                // exsactly which cells are affected.

                normLeft = (e.ClipRectangle.X - xOff) / width;
                xStart = Math.Max(normLeft, 0);
                xEnd = Math.Min(normLeft + (e.ClipRectangle.Width / width), gameBoard.Width);

                normTop = (e.ClipRectangle.Y - yOff) / width;
                yStart = Math.Max(normTop, 0);
                yEnd = Math.Min(normTop + (e.ClipRectangle.Height / width), gameBoard.Height);

            }
            else
            {
                // fractional cells in region -- hack: for now, we just tweak our standard 
                // calculations (which work when the ClipRectangle is on cell boundries) in
                // order to draw a extra surounding cells. This issues primary comes up when
                // windows spesifies the ClipRectangle because, e.g., the window has been clipped
                // by the screen edge

                normLeft = (e.ClipRectangle.X - xOff) / width;
                xStart = Math.Max(normLeft - 1, 0);
                xEnd = Math.Min(normLeft + (e.ClipRectangle.Width / width) + 2, gameBoard.Width);

                normTop = (e.ClipRectangle.Y - yOff) / width;
                yStart = Math.Max(normTop - 1, 0);
                yEnd = Math.Min(normTop + (e.ClipRectangle.Height / width) + 2, gameBoard.Height);
            }


#if DEBUG && PAINT_DEBUG
            Console.WriteLine("x: {0} +{1}    y: {2} +{3}", xStart, xEnd - xStart, yStart, yEnd - yStart);
#endif



            for (int x = xStart; x < xEnd; ++x)
            {
                for (int y = yStart; y < yEnd; ++y)
                {
                    ++cellsDrawn;

                    Cell cell = gameBoard[x, y];
                    if (cell == null) return;

                    cell.dirty = false;

                    if (cell.CellState == CellState.Hidden && !cell.Depressed)
                    {

                        if (!gameBoard.GameEnded || cell != gameBoard.CellThatExploded)
                        {
                            drawHiddenCell(x, y, e);
                        }

                    }
                    else if (cell.CellState == CellState.Flagged)
                    {
                        drawHiddenCell(x, y, e);
                        drawFlag(x, y, e);

                    }
                    else if (cell.CellState == CellState.Questioned)
                    {
                        // Draw question

                    }
                    else if (cell.CellState == CellState.Uncovered && cell.NeighboringMines > 0)
                    {

                        drawNumber(x, y, e);

                    }

                    if (gameBoard.PlayState == PlayState.Lost && cell.CellState != CellState.Flagged && !gameBoard.MineMap[x, y])
                    {

                        drawMine(x, y, e);
                    }

                    if (cell.Marked && cell.CellState != CellState.Uncovered)
                    {
                        drawMark(x, y, e, gameBoard[x, y].MarkColor);
                    }

                }
            }

#if DEBUG && PAINT_DEBUG
            Console.WriteLine("Cells drawn: {0}", cellsDrawn);
#endif


        }


        #region thin wrapers for drawing cell elements

        private void drawMark(int xCur, int yCur, System.Windows.Forms.PaintEventArgs e, Color color)
        {
            e.Graphics.DrawRectangle(new Pen(color), (xCur * width) + xOff + 2, (yCur * width) + yOff + 2, width - 6, width - 6);
        }

        private void drawFlag(int xCur, int yCur, System.Windows.Forms.PaintEventArgs e)
        {
            drawFlagAt((xCur * width) + xOff, (yCur * width) + yOff, e);
        }

        void drawMine(int xCur, int yCur, System.Windows.Forms.PaintEventArgs e)
        {
            if (xCur == gameBoard.CellThatExploded.X && yCur == gameBoard.CellThatExploded.Y)
            {
                drawMineAt((xCur * width) + xOff + 1, (yCur * width) + yOff + 1, e);
            }
            else
            {
                drawMineAt((xCur * width) + xOff, (yCur * width) + yOff, e);
            }

        }


        private void drawMineAt(int x, int y, System.Windows.Forms.PaintEventArgs e)
        {
            e.Graphics.DrawImage(mineBMP, x, y);
        }


        private void drawFlagAt(int x, int y, System.Windows.Forms.PaintEventArgs e)
        {
            e.Graphics.DrawImage(flagBMP, x, y);
        }


        private void drawNumber(int xCur, int yCur, System.Windows.Forms.PaintEventArgs e)
        {
            int mines = gameBoard[xCur, yCur].NeighboringMines;

            e.Graphics.DrawImage(numberCellBMP[mines], (xCur * width) + xOff, (yCur * width) + yOff);

        }

        private void drawHiddenCell(int xCur, int yCur, System.Windows.Forms.PaintEventArgs e)
        {
            Point cellLoc = new Point(this.xOff + (xCur * width), this.yOff + (yCur * width));
            e.Graphics.DrawImage(hiddenCellBMP, cellLoc.X, cellLoc.Y);
        }

        #endregion


        #region drawing resources

        Brush[] stringBrushes;
        void createStringBrushes()
        {
            int i = 0;


            stringBrushes = new Brush[9];


            stringBrushes[i++] = Brushes.Black;       // Mark text


            stringBrushes[i++] = Brushes.Blue;        // NeighboringMines == 1
            stringBrushes[i++] = Brushes.Green;       // NeighboringMines == 2
            stringBrushes[i++] = Brushes.Red;         // NeighboringMines == 3
            stringBrushes[i++] = Brushes.DarkBlue;    // NeighboringMines == 4
            stringBrushes[i++] = Brushes.DarkRed;     // NeighboringMines == 5
            stringBrushes[i++] = Brushes.DarkCyan;    // NeighboringMines == 6
            stringBrushes[i++] = Brushes.Black;       // NeighboringMines == 7
            stringBrushes[i++] = Brushes.Gray;        // NeighboringMines == 8

        }
        
        Bitmap hiddenCellBMP;
        void drawHiddenCellBMP()
        {
            if (hiddenCellBMP != null) hiddenCellBMP.Dispose();

            hiddenCellBMP = new Bitmap(width, width);
            Graphics g = Graphics.FromImage(hiddenCellBMP);

            Point p2 = new Point(0, 0);
            Point p1 = new Point(0, 0 + width);
            Point p3 = new Point(0 + width, 0);
            Point p4 = new Point(0 + width, 0 + width);

            p1.X += 1;
            p2.X += 1;

            p2.Y += 1;
            p3.Y += 1;

            Pen penL = new Pen(Color.White, 2.0f);
            g.DrawLine(penL, p1, p2);

            p2.X -= 1;
            g.DrawLine(penL, p2, p3);

            p1.X -= 1;
            p2.Y -= 1;
            p3.Y -= 1;

            Pen penD = new Pen(Color.DarkGray, 2.0f);

            p3.Y += 1;
            p3.X -= 1;
            p4.X -= 1;
            g.DrawLine(penD, p3, p4);


            p4.X += 1;
            p4.Y -= 1;
            p1.Y -= 1;
            p1.X += 1;
            g.DrawLine(penD, p4, p1);

            Brush brush = new SolidBrush(this.BackColor);

            g.FillRectangle(brush, p3.X - 1, p3.Y, 1, 1);
            g.FillRectangle(brush, p3.X, p3.Y - 1, 1, 1);

            g.FillRectangle(brush, p1.X - 1, p1.Y, 1, 1);
            g.FillRectangle(brush, p1.X, p1.Y - 1, 1, 1);

            g.Dispose();
        }

        Bitmap[] numberCellBMP;
        void drawNumberCellBMP()
        {
            if (numberCellBMP != null)
            {
                foreach (Bitmap bmp in numberCellBMP)
                    if (bmp != null) bmp.Dispose();
            }

            Graphics g;
            numberCellBMP = new Bitmap[9];

            Brush brush = new SolidBrush(this.BackColor);

            for (int mines = 1; mines < numberCellBMP.Length; ++mines)
            {
                numberCellBMP[mines] = new Bitmap(width, width);
                g = Graphics.FromImage(numberCellBMP[mines]);

                g.FillRectangle(brush, 2, 2, width - 4, width - 4);
                g.DrawString(mines.ToString(), new Font("Fopnmt", 8.8f, FontStyle.Bold), stringBrushes[mines],
                    3, 0);

                g.Dispose();
            }
        }

        // Smaller flag that works with out numbers
        Bitmap flagBMP;
        void drawFlagBMP()
        {
            if (flagBMP != null) flagBMP.Dispose();

            flagBMP = new Bitmap(width, width);
            Graphics g = Graphics.FromImage(flagBMP);

            int x = 0;
            int y = 0;

            x += 5;
            y += 4;

            Brush bBrush = new SolidBrush(Color.Black);
            Brush rBrush = new SolidBrush(Color.Red);

            Rectangle[] redRects = new Rectangle[2];
            redRects[0] = new Rectangle(x, y + 1, 2, 2);
            redRects[1] = new Rectangle(x + 2, y, 2, 4);

            Rectangle[] blackRects = new Rectangle[3];
            blackRects[0] = new Rectangle(x, y + 7, 6, 1);
            blackRects[1] = new Rectangle(x + 1, y + 6, 4, 1);
            blackRects[2] = new Rectangle(x + 3, y + 4, 1, 2);


            g.FillRectangles(rBrush, redRects);
            g.FillRectangles(bBrush, blackRects);

            g.Dispose();
        }

        // Exsact replica of Microsts flag (too dominating compared with our numbers)
        Bitmap flag2BMP;
        void drawFlag2BMP()
        {

            if (flag2BMP != null) flag2BMP.Dispose();

            flag2BMP = new Bitmap(width, width);
            Graphics g = Graphics.FromImage(flag2BMP);

            int x = 0;
            int y = 0;

            x += 4;
            y += 3;

            Brush bBrush = new SolidBrush(Color.Black);
            Brush rBrush = new SolidBrush(Color.Red);

            Rectangle[] redRects = new Rectangle[3];
            redRects[0] = new Rectangle(x, y + 2, 1, 1);
            redRects[1] = new Rectangle(x + 1, y + 1, 2, 3);
            redRects[2] = new Rectangle(x + 3, y, 2, 5);

            Rectangle[] blackRects = new Rectangle[3];
            blackRects[0] = new Rectangle(x, y + 8, 8, 2);
            blackRects[1] = new Rectangle(x + 2, y + 7, 4, 1);
            blackRects[2] = new Rectangle(x + 4, y + 5, 1, 2);


            g.FillRectangles(rBrush, redRects);
            g.FillRectangles(bBrush, blackRects);

            g.Dispose();
        }

        Bitmap mineBMP;
        void drawMineBMP()
        {
            if (mineBMP != null) mineBMP.Dispose();

            mineBMP = new Bitmap(width, width);
            Graphics g = Graphics.FromImage(mineBMP);

            int x = 0;
            int y = 0;

            x += 3;
            y += 3;

            Brush bBrush = new SolidBrush(Color.Black);
            Brush wBrush = new SolidBrush(Color.White);

            Rectangle[] blackRects = new Rectangle[9];
            blackRects[0] = new Rectangle(x + 1, y + 1, 1, 1);
            blackRects[1] = new Rectangle(x + 1, y + 7, 1, 1);
            blackRects[2] = new Rectangle(x + 7, y + 1, 1, 1);
            blackRects[3] = new Rectangle(x + 7, y + 7, 1, 1);

            blackRects[4] = new Rectangle(x + 0, y + 4, 2, 1);
            blackRects[5] = new Rectangle(x + 7, y + 4, 2, 1);

            blackRects[6] = new Rectangle(x + 4, y + 0, 1, 2);
            blackRects[7] = new Rectangle(x + 4, y + 7, 1, 2);

            // Big center rec
            blackRects[8] = new Rectangle(x + 2, y + 2, 5, 5);

            // White rec
            Rectangle whiteRec = new Rectangle(x + 3, y + 3, 1, 2);


            g.FillRectangles(bBrush, blackRects);
            g.FillRectangle(wBrush, whiteRec);

            g.Dispose();
        }

        #endregion



        public void InvalidateDirtyCells()
        {
            
            int xMin = int.MaxValue, xMax = int.MinValue;
            int yMin = int.MaxValue, yMax = int.MinValue;
            int dirtyCount = 0;

            foreach (Cell c in gameBoard.Field)
            {
                if (c == null) return;

                if (c.dirty)
                {
                    if (c.X < xMin) xMin = c.X;
                    if (c.X > xMax) xMax = c.X;

                    if (c.Y < yMin) yMin = c.Y;
                    if (c.Y > yMax) yMax = c.Y;

                    ++dirtyCount;
                }
            }

            if (dirtyCount > 0)
            {
                Invalidate(new Rectangle(xOff + (xMin * width), yOff + (yMin * width), (xMax - xMin + 1) * width, (yMax - yMin + 1) * width));
            }

        }


        #endregion



        #region Mouse Up/Down/Move + cell (single and block) depress/undepress

        protected void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        bool _isDepressed;
        public bool IsDepressed
        {
            get { return _isDepressed; }
            set
            {
                if (_isDepressed == value)
                    return;
                _isDepressed = value;
                OnPropertyChanged("IsDepressed");
            }
        }

        ArrayList curDepressedCells = new ArrayList(9);

        bool mouseLeftDown = false;
        bool mouseRightDown = false;
        bool mouseMiddleDown = false;

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (gameBoard == null) goto updateMouseButtonStates;

            if (gameBoard.GameEnded) return;


            if ((e.Button == MouseButtons.Right && mouseLeftDown) ||
                (e.Button == MouseButtons.Left && mouseRightDown) ||
                e.Button == MouseButtons.Middle)
            {
                // Midle, or left and right simotanius: Block Depress
                depressCells(e.X, e.Y, true);

            }
            else if (e.Button == MouseButtons.Left)
            {
                // Just left: Single cell depress
                depressCells(e.X, e.Y, false);
            }
            else if (e.Button == MouseButtons.Right)
            {
                // Just right: Flag
                Cell cell = translateMouseToCell(e.X, e.Y);

                if (cell != null)
                {
                    if (cell.CellState == CellState.Hidden)
                    {
                        gameBoard.SetCellState(cell, CellState.Flagged);
                    }
                    else if (cell.CellState == CellState.Flagged)
                    {
                        gameBoard.SetCellState(cell, CellState.Hidden);
                    }

                    OnUserChangedBoard();
                    Invalidate(new Rectangle(xOff + cell.X * width, yOff + cell.Y * width, width, width));
                }
            }

            updateMouseButtonStates:
            // Update mouse state
            if (e.Button == MouseButtons.Right)
            {
                mouseRightDown = e.Button == MouseButtons.Right;
            }
            else if (e.Button == MouseButtons.Left)
            {
                mouseLeftDown = e.Button == MouseButtons.Left;
            }
            else if (e.Button == MouseButtons.Middle)
            {
                mouseMiddleDown = (e.Button == MouseButtons.Middle);
            }

            // Update IsDepressed (used for worried smily face button)
            IsDepressed = curDepressedCells.Count > 0;
        }
        
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (gameBoard == null) goto updateMouseButtonStates;
 
            if (gameBoard.GameEnded) return;

            unDepressCells();

            Cell cell = translateMouseToCell(e.X, e.Y);

            if (cell != null)
            {
                bool gameNotOver = true;
                bool blockUncover = false;


                if ((e.Button == MouseButtons.Right && mouseLeftDown) ||
                    (e.Button == MouseButtons.Left && mouseRightDown) ||
                    e.Button == MouseButtons.Middle)
                {

                    blockUncover = true;

                    // Its a block: blockUncover, then undepress
                    gameNotOver = gameBoard.BlockUncover(cell);

                }
                else if (e.Button == MouseButtons.Left)
                {
                    // Its a sinlg cell: uncover
                    gameNotOver = gameBoard.Uncover(cell);
                }

                unDepressCells();


                if (gameNotOver)
                {
                    OnUserChangedBoard();

                    if (blockUncover || (cell.CellState == CellState.Uncovered && cell.NeighboringMines == 0))
                        InvalidateDirtyCells();
                }
                else
                {
                    InvalidateDirtyCells();

                }

            }

            updateMouseButtonStates:
            if (e.Button == MouseButtons.Right)
            {
                mouseRightDown = false;

            }
            else if (e.Button == MouseButtons.Left)
            {
                mouseLeftDown = false;

            }
            else if (e.Button == MouseButtons.Middle)
            {
                mouseMiddleDown = false;

            }

            // Update IsDepressed (used for worried smily face button)
            IsDepressed = curDepressedCells.Count > 0;
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (gameBoard == null || gameBoard.GameEnded) return;

            
            if (mouseMiddleDown || (mouseRightDown && mouseLeftDown))
            {
                depressCells(e.X, e.Y, true);
            }
            else if (mouseLeftDown)
            {
                depressCells(e.X, e.Y, false);
            }

            // Update IsDepressed (used for worried smily face button)
            IsDepressed = curDepressedCells.Count > 0;
        }
        
        private void depressCells(int mouseX, int mouseY, bool block)
        {
            Cell dCell = translateMouseToCell(mouseX, mouseY);

            // Depress if apropreate
            if (dCell != null)
            {
                ArrayList cellsToDepress;
                if (block)
                {
                    cellsToDepress = gameBoard.GetNeighbors(dCell);
                    cellsToDepress.Add(dCell);
                }
                else
                {
                    cellsToDepress = new ArrayList(1);
                    cellsToDepress.Add(dCell);
                }

                bool sameCells = false;
                if (curDepressedCells.Count > 0)
                {
                    sameCells = true;
                    foreach (Cell cell in curDepressedCells)
                    {
                        if (!cellsToDepress.Contains(cell))
                        {
                            sameCells = false;
                            break;
                        }
                    }
                }

                if (!sameCells)
                {
                    // unDepressOldCells
                    unDepressCells();

                    // depress new cells
                    curDepressedCells.AddRange(cellsToDepress);
                    foreach (Cell cell2 in curDepressedCells)
                    {
                        cell2.Depressed = true;
                    }

                    // Update screen
                    Cell c0 = curDepressedCells[0] as Cell;
                    if (curDepressedCells.Count > 1)
                    {
                        Invalidate(new Rectangle(xOff + c0.X * width, yOff + c0.Y * width, width * 3, width * 3));
                    }
                    else
                    {
                        Invalidate(new Rectangle(xOff + c0.X * width, yOff + c0.Y * width, width, width));
                    }


                }
                else
                {
                    // Nothing to do, thoes cells are already depresed
                }
            }
            else
            {
                // We are off the board, undepress cells
                unDepressCells();

                // Update screen
                InvalidateDirtyCells();
            }

        }

        private void unDepressCells()
        {
            if (curDepressedCells.Count == 0)
                return;

            foreach (Cell c in curDepressedCells)
            {
                c.Depressed = false;
            }

            Cell c0 = curDepressedCells[0] as Cell;
            if (curDepressedCells.Count > 1)
            {
                Invalidate(new Rectangle(xOff + c0.X * width, yOff + c0.Y * width, width * 3, width * 3));
            }
            else
            {
                Invalidate(new Rectangle(xOff + c0.X * width, yOff + c0.Y * width, width, width));
            }

            curDepressedCells.Clear();
        }

        private Cell translateMouseToCell(int mouseX, int mouseY)
        {
            int cellX = (mouseX - xOff) / width;
            int cellY = (mouseY - yOff) / width;

            if (cellX >= 0 && cellX < gameBoard.Field.GetLength(0) &&
                cellY >= 0 && cellY < gameBoard.Field.GetLength(1))
            {

                return gameBoard[(mouseX - xOff) / width, (mouseY - yOff) / width];
            }
            else
            {
                return null;
            }
        }


        #endregion




        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}
